package com.getan.boot.web.excel;

import com.alibaba.excel.EasyExcel;
import com.getan.boot.common.utils.Charsets;
import com.getan.boot.common.utils.StringPool;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

/**
 * Excel 导出工具类
 *
 * @author Bruce.Gong
 * @since 1.0.0-SNAPSHOT
 */
@Slf4j
public class ExportUtils {

    private static final String HEADER_DISPOSITION_TPL = "attachment;filename*=utf-8''%s.xlsx";

    /**
     * 简单导出，数据量 5000 以内，只输出一个 Sheet 页
     *
     * @param response 输出
     * @param fileName 文件名，不用带文件后缀，会自动添加 {@code .xlsx} 后缀
     * @param dataList 数据对象
     */
    public static <T> void export(HttpServletResponse response, String fileName, List<T> dataList, Class<T> templateClass) {
        export(response, fileName, fileName, dataList, templateClass);
    }

    /**
     * 简单导出，数据量 5000 以内，只输出一个 Sheet 页
     *
     * @param response  输出
     * @param fileName  文件名，不用带文件后缀，会自动添加 {@code .xlsx} 后缀
     * @param sheetName Sheet名称
     * @param dataList  数据对象
     */
    public static <T> void export(HttpServletResponse response, String fileName, String sheetName, List<T> dataList, Class<T> templateClass) {
        try {
            // 防止中文乱码
            fileName = URLEncoder.encode(fileName, Charsets.UTF_8_NAME).replaceAll("\\+", StringPool.HTTP_SPACE);
            // response 处理
            response.setContentType(StringPool.HTTP_CONTENT_TYPE_EXCEL);
            response.setCharacterEncoding(Charsets.UTF_8_NAME);
            response.setHeader(StringPool.HTTP_HEADER_DISPOSITION, String.format(HEADER_DISPOSITION_TPL, fileName));
            // 写入
            EasyExcel.write(response.getOutputStream(), templateClass)
                    .autoCloseStream(true)
                    .needHead(true)
                    .registerWriteHandler(new CustomWriteHandler())
                    .useDefaultStyle(false)
                    .sheet(sheetName)
                    .doWrite(dataList);
        } catch (UnsupportedEncodingException e) {
            log.error("数据导出 URLEncoder 失败", e);
        } catch (IOException e) {
            log.error("数据导出 IO 异常", e);
        } catch (Exception e) {
            log.error("数据导出失败", e);
        }
    }

    /**
     * 简单导出，数据量 5000 以内，只输出一个 Sheet 页
     *
     * @param response 输出
     * @param fileName 文件名，不用带文件后缀，会自动添加 {@code .xlsx} 后缀
     * @param dataList 数据对象
     */
    public static <T> void export(HttpServletResponse response, String fileName, List<String> headers, List<List<Object>> dataList) {
        try {
            // 防止中文乱码
            fileName = URLEncoder.encode(fileName, Charsets.UTF_8_NAME).replaceAll("\\+", StringPool.HTTP_SPACE);
            // response 处理
            response.setContentType(StringPool.HTTP_CONTENT_TYPE_EXCEL);
            response.setCharacterEncoding(Charsets.UTF_8_NAME);
            response.setHeader(StringPool.HTTP_HEADER_DISPOSITION, String.format(HEADER_DISPOSITION_TPL, fileName));
            // 写入
            EasyExcel.write(response.getOutputStream())
                    .autoCloseStream(true)
//                    .needHead(true)
                    .registerWriteHandler(new CustomWriteHandler())
                    .useDefaultStyle(false)
                    .head(generateHeader(headers))
                    .sheet(fileName)
                    .doWrite(dataList);
        } catch (UnsupportedEncodingException e) {
            log.error("数据导出 URLEncoder 失败", e);
        } catch (IOException e) {
            log.error("数据导出 IO 异常", e);
        } catch (Exception e) {
            log.error("数据导出失败", e);
        }
    }

    public static List<List<String>> generateHeader(List<String> headers) {
        return headers.stream().map(Collections::singletonList).collect(Collectors.toList());
    }

    public static <T> List<List<Object>> generateCell(List<T> result, BiConsumer<List<Object>, T> rowRender) {
        List<List<Object>> list = Lists.newArrayListWithCapacity(result.size());
        for (T t : result) {
            List<Object> data = Lists.newArrayList();
            rowRender.accept(data, t);
            list.add(data);
        }
        return list;
    }

}
